﻿/* ShellExecuteAが使いたいが為のもの */
#define _WIN32_WINNT	0x0501
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <ShellAPI.h>
/**/

#define OAUTH_USE_SSL						// protocol::(async_)httpsを有効に

#pragma warning( push )
#pragma warning( disable:4065 )
#pragma warning( disable:4101 )
#pragma warning( disable:4309 )
#pragma warning( disable:4503 )
#pragma warning( disable:4996 )

# include "include/clients/twitter.hpp"         // twitterAPI一式 
# include "include/clients/twitpic.hpp"         // twitpicAPI一式 
# include "include/clients/hatena.hpp"          // HatenaAPI一式 
//# include "include/clients/google.hpp"          // GoogleAPI一式 
#pragma warning( pop )


void showtree( const webapp::container::response& res, int depth = 0 );




#include <iostream>


int main( int argv, char* argc[] )
{
	using namespace webapp;
	using namespace webapp::clients;


	// サンプル集
	// 動かないのあったら教えて！

	// webapp::clients::<hogehoge>::client の使い方
	// client<> or client<protocol::http>	//普通なの
	// client<protocol::https>				//HTTPSが使いたいなら OAUTH_USE_SSL をdefineしてこっちを使ってください。
	// client<protocol::async_http>			//HTTPで非同期で動かしたいならこっち
	// client<protocol::async_https>		//もちろんこれも



	// ------------------------------------------------------------------------------------------
	//                               --------------------------------------------------------------
	// hatenaの認証サンプル -------------------------------------------------------------------------
	//                               ------------------------------------------------------------------
	// --------------------------------------------------------------------------------------------------

	const oauth::v1::key<> mdh( "- consumer_key -" , "- consumer_secret -" , "- token -" , "- secret -" );


	// クライアントを作ってみる
	hatena::client<protocol::https::sync> ht( mdh );
	ht.request<hatena::api::diary::atom>( "" );

#if 0
	// -- consumer_key, consumer_secret
	const oauth::v1::key hatena_key( "--consumer_key--", "--consumer_secret--" );

	hatena::authenticator<> auth( hatena_key );
	const auto request_token = auth.get_request_token( "scope=write_public" );
	std::cout << "is_complete : " << auth.is_complete() << std::endl;
	if ( request_token ) {
		::ShellExecuteA( NULL, "open", auth.get_authorize_url( request_token->first ).c_str(), 0, 0, 0 );
		
		std::string pin;
		std::cout << "pin: " << std::flush;
		std::cin >> pin;

		const auto access_token = auth.get_access_token( pin, *request_token );
		std::cout << "is_complete : " << auth.is_complete() << std::endl;

		if ( access_token ) {
			std::cout << "url_name     => " << access_token->first << std::endl;
			std::cout << "display_name => " << access_token->second << std::endl;
		} else {
			std::cout << "failed. (access token)" << std::endl;
			return -1;
		}
	} else {
		std::cout << "failed. (request token)" << std::endl;
		return -1;
	}
#endif



	// ------------------------------------------------------------------------------------------
	//                               --------------------------------------------------------------
	// googleの認証サンプル --- OAuth 2.0で！ -------------------------------------------------------
	//                               ------------------------------------------------------------------
	// --------------------------------------------------------------------------------------------------

#if 0
	// -- client_id , client_secret
	const oauth::v2::key_v2 google_key( "--client_id--", "--client_secret--" );

	google::authenticator<protocol::https> google_auth( google_key );
	::ShellExecuteA(
		NULL,
		"open",
		google_auth.get_authorize_url( "code", "urn:ietf:wg:oauth:2.0:oob", "scope=https://mail.google.com/mail/feed/atom/" ).c_str(),
		0, 0, 0 );

	std::string code;
	std::cout << "code: " << std::flush;
	std::cin >> code;

	// grant_type - そのgrant_typeのパラメータ1 - そのgrant_typeのパラメータ2 - オプション
	/*
		第一引数が"refresh_token"でかつoptionを指定する場合は、
		google_auth.get_access_token( "refresh_token", code, "", option );
		のように第三引数を空に指定してください。
	*/
	google_auth.get_access_token( "authorization_code", code, "urn:ietf:wg:oauth:2.0:oob" );
#endif



	// ------------------------------------------------------------------------------------------
	//                               --------------------------------------------------------------
	// twitterのクライアントサンプル ----------------------------------------------------------------
	//                               ------------------------------------------------------------------
	// --------------------------------------------------------------------------------------------------


#if 1	// - 最初から全部知ってますけど。な場合

	// キーデータ
	const oauth::v1::key<> md( "- consumer_key -" , "- consumer_secret -" , "- token -" , "- secret -" );

	// クライアントを作ってみる
	twitter::client<protocol::https::sync> tw( md );
	

#else	// - コンシューマのキーしか知らない場合

	//認証のみHTTPSで同期的に行い、その情報を用いて非同期のオブジェクトを作成するよ！

	// -- consumer_key, consumer_secret
	const oauth::v1::key<> md( "--consumer_key--", "--consumer_secret--" );

	twitter::authenticator<> auth( md );
	//oauth::common_authenticator<protocol::https,twitter::policy::authenticator> auth( md );
	const auto request_token = auth.get_request_token();
	if ( request_token ) {
		::ShellExecuteA( NULL, "open", auth.get_authorize_url( request_token->first ).c_str(), 0, 0, 0 );
		
		std::string pin;
		std::cout << "pin: " << std::flush;
		std::cin >> pin;

		const auto access_token = auth.get_access_token( pin, *request_token );
		if ( access_token ) {
			std::cout << "user_id     => " << (*access_token).first << std::endl;
			std::cout << "screen_name => " << (*access_token).second << std::endl;
		} else {
			std::cout << "failed. (auth)" << std::endl;
			return -1;
		}
	} else {
		std::cout << "failed. (request token)" << std::endl;
		return -1;
	}


	//非同期のオブジェクト
	twitter::client<protocol::async_https> tw( auth.get_auth_key() );

	

#endif
	
	//
	const auto func = boost::bind(
				boost::function<void ( const container::response&, const error::value& )>(
				[]( const container::response& res, const error::value& error )
				{
					if ( !error ) {
						std::cout << "コールバック！" << std::endl;
						showtree( res );
						//std::cout << (std::string)res["9"]["user"]["name"] << std::endl;
						std::cout << "コールバックでした！" << std::endl;
					} else {
						std::cout << "error! : " << error.get_code() << " - " << error.get_message() << std::endl;
						
					}
				} )
					, webapp::placeholders::response, webapp::placeholders::error
				);

	//
	const auto streaming_func = boost::bind(
				boost::function<void ( const std::string&, const error::value& )>(
				[]( const std::string& res, const error::value& error )
				{
					if ( !error ) {
						std::cout << "stream！" << std::endl;
						std::cout << res << std::endl;
					} else {
						std::cout << "error! : " << error.get_code() << " - " << error.get_message() << std::endl;
						
					}
				} )
					, webapp::placeholders::value, webapp::placeholders::error
				);
				

#if 0
	twitter::stream<protocol::http::async> s1( basic::key( "--screen_name--", "--password--" ) );
	

	s1.start<twitter::api::streaming::sample>( "json", "512", streaming_func );

	boost::thread::sleep( boost::get_system_time() + boost::posix_time::milliseconds( 4000 ) );
	std::cout << "slept!" << std::endl;
	s1.stop();

	boost::thread::sleep( boost::get_system_time() + boost::posix_time::milliseconds( 1000 ) );
	std::cout << "もっかい!" << std::endl;

	s1.start<twitter::api::streaming::sample>( "json", "512", streaming_func );

	boost::thread::sleep( boost::get_system_time() + boost::posix_time::milliseconds( 2000 ) );
	std::cout << "slept!" << std::endl;
	s1.stop();
#endif



	//単純な投稿のサンプル！どれも同じ結果になるはず。
	//結果はresponse型で欲しいのでxml_tree_helperを使ってます。
#if 0
	{
		// ヘルパを使って
		//   これから増やしていきます；　リスト系API以外はこの方法が一番オススメです。
		//   ヘルパの種類はinclude/clients/twitter/request.hppを参照してください。
		/*tw.request_xml<twitter::api::statuses::update>( "ゲソ！",
			boost::bind(
				boost::function<void ( const response& )>(
				[]( const response& res ) {
					std::cout << "コールバック！" << std::endl;
					showtree( res );
					std::cout << "コールバックでした！" << std::endl;
				} )
				, webapp::placeholders::response )
			);*/

	//	error::value error;



		//auto res = xml_tree_helper( tw.request<twitter::api::statuses::update>( "xml", "ゲソ！" ) );

		//
		tw.request_xml<twitter::api::statuses::update>( "テストでゲソ", func );
		

		// std::mapから、をやりたかった
		//   ヘルパが対応してない場合はこっちを使うのも手
		//auto res = xml_tree_helper( tw.raw_request( "POST", "statuses/update", "xml", (param_type)boost::assign::map_list_of( "status", "ゲソ！" ) ) );

		// 文字列から
		//   一番手軽で自由。不具合あったら教えてください。
		//auto res = xml_tree_helper( tw.raw_request( "POST", "statuses/update", "xml", "status=ゲソ！", error ) );

	//	showtree( res );
	}
#endif
	

	//単純な投稿のサンプル2！
	//@yutoppのツイート(id=28660250458456064)に（ツイート位置情報が許可されていれば）吉祥寺の位置情報を含んでリプライを飛ばします。
#if 1
	{
		//const auto f = boost::function<void (const std::string&)>( []( const std::string& res ){ showtree( webapp::xml_tree_helper( res ) ); }, _1 );

		// ヘルパを使って
		//tw.request_xml<twitter::api::statuses::update>( "@yutopp もいっちょテスト！", "28660250458456064", "35.702811", "139.579804", []( const response& res ){ showtree( res ); } );
		//auto res = xml_tree_helper( tw.request<twitter::api::statuses::update>( "xml", f, "@yutopp ほげ！", "28660250458456064", "35.702811", "139.579804" ) );

		// std::mapから
		/*auto res = xml_tree_helper( tw.raw_request( "POST", "statuses/update", "xml"
			, (param_type)boost::assign::map_list_of
				( "status",					"@yutopp ほげ！" )
				( "in_reply_to_status_id",	"28660250458456064" )
				( "lat",					"35.702811" )
				( "long",					"139.579804" )
			) );*/
		error::value e;

/*		tw.raw_urlencoded_request(
			"POST",
			"statuses/update",
			"xml",
			"status=@yutopp てっすてっす！&in_reply_to_status_id=28660250458456064&lat=35.702811&long=139.579804",
			e );*/

/*		// 文字列から
		tw.raw_urlencoded_request(
			  "POST"
			, "statuses/update"
			, "xml"
			, "status=@yutopp てっすてっす！&in_reply_to_status_id=28660250458456064&lat=35.702811&long=139.579804"
			/*, e/*boost::bind( boost::function<void (const error::value&)>(
				[&]( const error::value& error ){
					std::cout
						<< "コールバック！ : "
						<< error.get_code() << " - " << error.get_message()
						<< std::endl;
				} ),
				webapp::placeholders::error )*/
//			);*/

		//showtree( res );
	}
#endif



	//リスト系APIのサンプル！
	//リスト作っちゃうよ！
#if 1
	{
		const std::string screen_name( "yutopp" );	//自分のスクリーン名でも入れて！
		/*auto res = xml_tree_helper( tw.raw_urlencoded_request( "POST", screen_name + "/lists", "xml", "name=gesogeso&mode=private&description=リスト作成のテストでゲソ" ) );

		showtree( res );/**/

		tw.request_xml<twitter::api::list::create>( screen_name, "ちくわ2", "public", "リスト作成のテスト4回目でゲソ！"/*, func*/ );

		//tw.request_xml<twitter::api::list::update>( screen_name, "gesogeso4th", "updates-test", "public"/*"private"*/, "リストupdateのテスト4", func );

		//tw.request_xml<twitter::api::list::index>( screen_name, "-1", func );

		//tw.request_xml<twitter::api::list::show>( screen_name, "gesogeso3rd", func );

		//tw.request_xml<twitter::api::list::destroy>( screen_name, "updates-test", func );

		//tw.request_xml<twitter::api::list::statuses>( screen_name, "gesogeso3rd", func );

		//tw.request_xml<twitter::api::list::memberships>( screen_name, func );

		//tw.request_xml<twitter::api::list::subscriptions>( screen_name, func );

		//tw.request_xml<twitter::api::list::get_member>( screen_name, "gesogeso3rd", func );

		//tw.request_xml<twitter::api::list::add_member>( screen_name, "gesogeso3rd", "???", func );
		
		//tw.request_xml<twitter::api::list::delete_member>( screen_name, "gesogeso3rd", "ここはid（数字）", func );

		//tw.request_xml<twitter::api::list::is_member>( screen_name, "gesogeso3rd", "ここはid（数字）", func );

	}


#endif


	//自分のアイコン変更など
#if 0
	{
		tw.request_xml<twitter::api::account::update_profile_image>( "Y.png",
			boost::bind(
				boost::function<void ( const response& )>(
				[]( const response& res ) {
					std::cout << "コールバック！" << std::endl;
					showtree( res );
					std::cout << "コールバックでした！" << std::endl;
				} )
				, webapp::placeholders::response )
			);
		//showtree( res );
	}
#endif

	//タイムライン取得
#if 0
	{
		//tw.request_xml<twitter::api::statuses::home_timeline>( func );
		tw.request_xml<twitter::api::statuses::home_timeline>( func );

		//json_tree_helper( std::string( "{ \"hoge\" : \"\\u4587g  g\" }" ) );

	}
#endif

	// ------------------------------------------------------------------------------------------
	//                               --------------------------------------------------------------
	// twitpicのクライアントサンプル ----------------------------------------------------------------
	//                               ------------------------------------------------------------------
	// --------------------------------------------------------------------------------------------------

	//一応v2のAPI全てをヘルパで呼べるようになっていますがミスがあるかもしれないですのよ

	// -- clients::twitterのインスタンス, twitpicアプリのtoken
	//twitpic::client<protocol::async_https> tp( tw, "-token-" );

	//画像のアップロード
#if 0
	{
		// メッセージ, ファイルパス
		//auto res = tp.request_xml<twitpic::api::upload>( "おかしーし・・・2", "Y.png" );
		//showtree( res );	// resが正しくない；
		// メッセージ, ファイルパス
		tp.request_xml<twitpic::api::upload>( "-token-", "test・・・", "Y.png", func );
		
	}
#endif

	//コメントの投稿
#if 0
	{
		// media_id, コメント
		auto res = tp.request_xml<twitpic::api::comments::create>( "3uoohv", "おかしくねーし！" );
		showtree( res );	// resが正しくない；
	}
#endif

	//イベントの作成
#if 0
	{
		// name, description, trigger
		auto res = tp.request_xml<twitpic::api::event::create,bool>( "test_event", "hoge!", "#piyo" );
		showtree( res );	// resが正しくない；
	}
#endif

	std::cout << "------------------------------" << std::endl;
	std::cout << "finished!" << std::endl;

	{
		int i;
		std::cin >> i;
	}

	return 0;
}

void showtree( const webapp::container::response& res, int depth )
{
	BOOST_FOREACH( const auto& var, res ) {
		std::cout << std::string( depth, ' ' ) << ( var->get_key() ? *var->get_key() : "?undefined?" ) << " : " << std::endl;
		if ( var->is_leaf() ) {
			std::cout << std::string( depth, ' ' ) << "=>" << *var->get_value() << std::endl;
		} else {
			showtree( *var, depth + 1 );
		}
	}
}